home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
gnu
/
mtools.lha
/
mtools-2.0.7
/
init.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-09-10
|
8KB
|
364 lines
/*
* Initialize an MSDOS diskette. Read the boot sector, and switch to the
* proper floppy disk device to match the format on the disk. Sets a bunch
* of global variables. Returns 0 on success, or 1 on failure.
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "msdos.h"
#define FULL_CYL
#define WORD(x) ((boot->x)[0] + ((boot->x)[1] << 8))
#define DWORD(x) ((boot->x)[0] + ((boot->x)[1] << 8) + ((boot->x)[2] << 16) + ((boot->x)[3] << 24))
unsigned int num_clus; /* total number of cluster */
int num_fat; /* the number of FAT tables */
long disk_offset; /* skip this many bytes */
int fat_bits; /* the FAT encoding scheme */
extern int fd, fat_len, dir_len, dir_start, clus_size, dir_dirty, disk_dirty;
extern int fat_error, disk_size;
extern long disk_current;
extern char *mcwd;
extern unsigned char *fat_buf, *disk_buf, *dir_buf;
extern struct device devices[];
static struct bootsector *read_boot();
int
init(drive, mode)
char drive;
int mode;
{
int fat_start, tracks, heads, sectors, old_dos;
char buf[256], *malloc(), *name, *expand();
void perror(), exit(), reset_chain(), free(), fat_read();
struct bootsector *boot;
struct device *dev;
if (fd != -1) {
close(fd);
free((char *) fat_buf);
free((char *) disk_buf);
free((char *) dir_buf);
}
/* check out the drive letter */
dev = devices;
while (dev->drive) {
if (dev->drive == drive)
break;
dev++;
}
if (!dev->drive) {
fprintf(stderr, "Drive '%c:' not supported\n", drive);
return(1);
}
/* open the device */
while (dev->name) {
if (dev->drive != drive)
break;
name = expand(dev->name);
if ((fd = open(name, mode | dev->mode)) < 0) {
sprintf(buf, "init: open \"%s\"", name);
perror(buf);
exit(1);
}
/* lock the device on writes */
if (mode == 2 && lock_dev(fd)) {
fprintf(stderr, "Device \"%s\" is busy\n", dev->name);
exit(1);
}
/* set default parameters, if needed */
if (dev->gioctl) {
if ((*(dev->gioctl)) (fd, dev->tracks, dev->heads, dev->sectors))
goto try_again;
}
/* read the boot sector */
disk_offset = dev->offset;
if ((boot = read_boot()) == NULL)
goto try_again;
heads = WORD(nheads);
sectors = WORD(nsect);
if (heads && sectors)
tracks = WORD(psect) / (unsigned) (heads * sectors);
/* sanity checking */
old_dos = 0;
if (!heads || heads > 100 || !sectors || sectors > 500 || tracks > 5000 || !boot->clsiz) {
/*
* The above technique will fail on diskettes that
* have been formatted with very old MSDOS, so we
* resort to the old table-driven method using the
* media signature (first byte in FAT).
*/
unsigned char temp[MSECTOR_SIZE];
if (read(fd, (char *) temp, MSECTOR_SIZE) != MSECTOR_SIZE)
temp[0] = '0';
switch (temp[0]) {
case 0xfe: /* 160k */
tracks = 40;
sectors = 8;
heads = 1;
dir_start = 3;
dir_len = 4;
clus_size = 1;
fat_len = 1;
num_clus = 313;
break;
case 0xfc: /* 180k */
tracks = 40;
sectors = 9;
heads = 1;
dir_start = 5;
dir_len = 4;
clus_size = 1;
fat_len = 2;
num_clus = 351;
break;
case 0xff: /* 320k */
tracks = 40;
sectors = 8;
heads = 2;
dir_start = 3;
dir_len = 7;
clus_size = 2;
fat_len = 1;
num_clus = 315;
break;
case 0xfd: /* 360k */
tracks = 40;
sectors = 9;
heads = 2;
dir_start = 5;
dir_len = 7;
clus_size = 2;
fat_len = 2;
num_clus = 354;
break;
default:
fprintf(stderr, "Probable non-MSDOS disk\n");
close(fd);
fd = -1;
return(1);
}
fat_start = 1;
num_fat = 2;
old_dos = 1;
}
/* check the parameters */
if (dev->tracks && !dev->gioctl) {
if (dev->tracks == tracks && dev->heads == heads && dev->sectors == sectors)
break;
}
else
break;
try_again: close(fd);
fd = -1;
dev++;
}
if (fd == -1) {
if (boot != NULL && dev->tracks)
fprintf(stderr, "No support for %d tracks, %d heads, %d sector diskettes\n", tracks, heads, sectors);
return(1);
}
/* set new parameters, if needed */
if (dev->gioctl) {
if ((*(dev->gioctl)) (fd, tracks, heads, sectors)) {
fprintf(stderr, "Can't set disk parameters\n");
close(fd);
fd = -1;
return(1);
}
}
/*
* all numbers are in sectors, except num_clus (which is in clusters)
*/
if (!old_dos) {
clus_size = boot->clsiz;
fat_start = WORD(nrsvsect);
fat_len = WORD(fatlen);
dir_start = fat_start + (boot->nfat * fat_len);
dir_len = WORD(dirents) * MDIR_SIZE / (unsigned) MSECTOR_SIZE;
/*
* For DOS partitions > 32M
*/
if (WORD(psect) == 0)
num_clus = (unsigned int) (DWORD(bigsect) - dir_start - dir_len) / clus_size;
else
num_clus = (unsigned int) (WORD(psect) - dir_start - dir_len) / clus_size;
num_fat = boot->nfat;
}
/* more sanity checking */
if (clus_size * MSECTOR_SIZE > MAX_CLUSTER) {
fprintf(stderr, "Cluster size of %d is larger than max of %d\n", clus_size * MSECTOR_SIZE, MAX_CLUSTER);
close(fd);
fd = -1;
return(1);
}
if (!old_dos && WORD(secsiz) != MSECTOR_SIZE) {
fprintf(stderr, "Sector size of %d is not supported\n", WORD(secsiz));
close(fd);
fd = -1;
return(1);
}
/* full cylinder buffering */
#ifdef FULL_CYL
disk_size = (dev->tracks) ? (sectors * heads) : 1;
#else /* FULL_CYL */
disk_size = (dev->tracks) ? sectors : 1;
#endif /* FULL_CYL */
disk_buf = (unsigned char *) malloc((unsigned int) disk_size * MSECTOR_SIZE);
if (disk_buf == NULL) {
perror("init: malloc");
exit(1);
}
/* read the FAT sectors */
disk_current = -1000L;
disk_dirty = 0;
fat_error = 0;
fat_bits = dev->fat_bits;
fat_read(fat_start);
/* set dir_chain[] to root directory */
dir_dirty = 0;
reset_chain(NEW);
return(0);
}
/*
* Fix the info in the MCWD file to be a proper directory name. Always
* has a leading separator. Never has a trailing separator (unless it is
* the path itself).
*/
char *
fix_mcwd()
{
FILE *fp;
struct stat sbuf;
char *s, *strcpy(), *strcat(), *mcwd_path, *getenv(), *strncpy();
char buf[BUFSIZ], *file, *expand();
static char ans[MAX_PATH];
long now, time();
mcwd_path = getenv("MCWD");
if (mcwd_path == NULL || *mcwd_path == '\0')
mcwd_path = "$HOME/.mcwd";
file = expand(mcwd_path);
if (stat(file, &sbuf) < 0)
return("A:/");
/*
* Ignore the info, if the file is more than 6 hours old
*/
time(&now);
if (now - sbuf.st_mtime > 6 * 60 * 60) {
fprintf(stderr, "Warning: \"%s\" is out of date, contents ignored\n", file);
return("A:/");
}
if (!(fp = fopen(file, "r")))
return("A:/");
if (!fgets(buf, BUFSIZ, fp))
return("A:/");
buf[strlen(buf) -1] = '\0';
fclose(fp);
/* drive letter present? */
s = buf;
if (buf[0] && buf[1] == ':') {
strncpy(ans, buf, 2);
ans[2] = '\0';
s = &buf[2];
}
else
strcpy(ans, "A:");
/* add a leading separator */
if (*s != '/' && *s != '\\') {
strcat(ans, "/");
strcat(ans, s);
}
else
strcat(ans, s);
/* translate to upper case */
for (s = ans; *s; ++s) {
if (islower(*s))
*s = toupper(*s);
if (*s == '\\')
*s = '/';
}
/* if only drive, colon, & separator */
if (strlen(ans) == 3)
return(ans);
/* zap the trailing separator */
if (*--s == '/')
*s = '\0';
return(ans);
}
/*
* Read the boot sector. We glean the disk parameters from this sector.
*/
static struct bootsector *
read_boot()
{
long lseek();
static struct bootsector boot;
if (lseek(fd, disk_offset, 0) < 0)
return(NULL);
/* read the first sector */
if (read(fd, (char *) &boot, MSECTOR_SIZE) != MSECTOR_SIZE)
return(NULL);
return(&boot);
}
/*
* Create an advisory lock on the device to prevent concurrent writes.
* Uses either lockf, flock, or fcntl locking methods. See the Makefile
* and the Configure files for how to specify the proper method.
*/
static int
lock_dev(fd)
int fd;
{
#ifdef LOCKF
#include <unistd.h>
if (lockf(fd, F_TLOCK, 0) < 0)
return(1);
#endif /* LOCKF */
#ifdef FLOCK
#include <sys/file.h>
if (flock(fd, LOCK_EX|LOCK_NB) < 0)
return(1);
#endif /* FLOCK */
#ifdef FCNTL
#include <fcntl.h>
struct flock flk;
flk.l_type = F_WRLCK;
flk.l_whence = 0;
flk.l_start = 0L;
flk.l_len = 0L;
if (fcntl(fd, F_SETLK, &flk) < 0)
return(1);
#endif /* FCNTL */
return(0);
}